ControlService (advapi32)
Last changed: 74.74.236.101

.
Summary
The ControlService function sends a control code to a service.

C# Signature:

[DllImport("advapi32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool    ControlService( IntPtr         hService,
                       SERVICE_CONTROL    dwControl,
                       ref SERVICE_STATUS    lpServiceStatus
                                                    );

VB Signature:

Declare Function ControlService Lib "advapi32.dll" (ByVal hService As Long, ByVal lControlCode As Long, ByVal lpServiceStatus As SERVICE_STATUS) As Boolean

C# User-Defined Types:

See SERVICE_STATUS definition

[Flags]
public enum SERVICE_CONTROL : uint
{
    STOP           = 0x00000001,
    PAUSE          = 0x00000002,
    CONTINUE           = 0x00000003,
    INTERROGATE        = 0x00000004,
    SHUTDOWN           = 0x00000005,
    PARAMCHANGE        = 0x00000006,
    NETBINDADD         = 0x00000007,
    NETBINDREMOVE      = 0x00000008,
    NETBINDENABLE      = 0x00000009,
    NETBINDDISABLE     = 0x0000000A,
    DEVICEEVENT        = 0x0000000B,
    HARDWAREPROFILECHANGE  = 0x0000000C,
    POWEREVENT         = 0x0000000D,
    SESSIONCHANGE      = 0x0000000E
}

public enum SERVICE_STATE : uint
{
    SERVICE_STOPPED            = 0x00000001,
    SERVICE_START_PENDING          = 0x00000002,
    SERVICE_STOP_PENDING           = 0x00000003,
    SERVICE_RUNNING            = 0x00000004,
    SERVICE_CONTINUE_PENDING           = 0x00000005,
    SERVICE_PAUSE_PENDING          = 0x00000006,
    SERVICE_PAUSED             = 0x00000007
}

[Flags]
public enum SERVICE_ACCEPT : uint
{
    STOP            = 0x00000001,
    PAUSE_CONTINUE      = 0x00000002,
    SHUTDOWN        = 0x00000004,
    PARAMCHANGE         = 0x00000008,
    NETBINDCHANGE       = 0x00000010,
    HARDWAREPROFILECHANGE   = 0x00000020,
    POWEREVENT          = 0x00000040,
    SESSIONCHANGE       = 0x00000080,
}

VB User-Defined Types:

See SERVICE_STATUS definition

Public Enum SERVICE_CONTROL As Integer

    [STOP] = &H1
    PAUSE = &H2
    CONTINUE = &H3
    INTERROGATE = &H4
    SHUTDOWN = &H5
    PARAMCHANGE = &H6
    NETBINDADD = &H7
    NETBINDREMOVE = &H8
    NETBINDENABLE = &H9
    NETBINDDISABLE = &HA
    DEVICEEVENT = &HB
    HARDWAREPROFILECHANGE = &HC
    POWEREVENT = &HD
    SESSIONCHANGE = &HE

End Enum

Public Enum SERVICE_STATE As Integer

    SERVICE_STOPPED = &H1
    SERVICE_START_PENDING = &H2
    SERVICE_STOP_PENDING = &H3
    SERVICE_RUNNING = &H4
    SERVICE_CONTINUE_PENDING = &H5
    SERVICE_PAUSE_PENDING = &H6
    SERVICE_PAUSED = &H7

End Enum

Public Enum SERVICE_ACCEPT As Integer

    [STOP] = &H1
    PAUSE_CONTINUE = &H2
    SHUTDOWN = &H4
    PARAMCHANGE = &H8
    NETBINDCHANGE = &H10
    HARDWAREPROFILECHANGE = &H20
    POWEREVENT = &H40
    SESSIONCHANGE = &H80

End Enum

Notes:

None.

Tips & Tricks:

Sample Code:

C#

IntPtr con = Tools.OpenSCManager("\\\\compname", null, (uint)SCM_ACCESS.SC_MANAGER_ALL_ACCESS);
IntPtr serv = Tools.OpenService(con, "ServiceTest", (uint)SERVICE_ACCESS.SERVICE_ALL_ACCESS);
SERVICE_STATUS stat = new SERVICE_STATUS();
bool res = Tools.ControlService(m_serv, SERVICE_CONTROL.STOP, ref stat);
Console.WriteLine("Stop service result: " + res);
// TODO: Code to halt execution until the service has finally stopped, to continue another task afterwards.

Alternative Managed API:

C#

using System;
using System.Runtime.InteropServices;
using System.Reflection;
using System.ServiceProcess;
using System.Threading;


namespace ServiceHelper
{
     /// <summary>
     /// Helper class for service control.
     /// </summary>
     public class ServiceControl : IDisposable
     {
     private int WAIT_TIMEOUT = 0x00000102;

     private IntPtr serviceControlManagerHandle;
     private IntPtr serviceHandle;
     private IntPtr statusHandle;

     private Thread heardbeatThread;

     private ManualResetEvent heardbeatStopped;
     private ManualResetEvent heardbeatActive;
     private ManualResetEvent terminationPending;

     private uint waitHintMilliseconds;
     private int heardbeatMilliseconds;

     public ServiceControl (ServiceBase aServiceBase, uint aWaitHintMilliseconds, int aHeardbeatMilliseconds)
     {
         FieldInfo fieldInfo;
         BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;
         SERVICE_STATUS serviceStatus;

         serviceControlManagerHandle = Interop.OpenSCManager (
         null,
         null,
         (uint) SCM_ACCESS.SC_MANAGER_ALL_ACCESS);

         if (serviceControlManagerHandle == IntPtr.Zero)
         {
         throw new Exception (aServiceBase.ServiceName);
         }

         serviceHandle = Interop.OpenService (
         serviceControlManagerHandle,
         aServiceBase.ServiceName,
        (uint) SERVICE_ACCESS.SERVICE_ALL_ACCESS);

         if (serviceHandle == IntPtr.Zero)
         {
         throw new Exception ("aServiceName");
         }

         fieldInfo = typeof (System.ServiceProcess.ServiceBase).GetField ("statusHandle", bindingFlags);

         statusHandle = (IntPtr) (fieldInfo.GetValue (aServiceBase));

         if (statusHandle == IntPtr.Zero)
         throw new Exception ("statusHandle is not valid.");

         waitHintMilliseconds = aWaitHintMilliseconds;
         heardbeatMilliseconds = aHeardbeatMilliseconds;

         serviceStatus = new SERVICE_STATUS ();

         Interop.QueryServiceStatus (serviceHandle, ref serviceStatus);
         serviceStatus.WaitHint = waitHintMilliseconds;
         Interop.SetServiceStatus (statusHandle, ref serviceStatus);

         terminationPending = new ManualResetEvent (false);
         heardbeatStopped = new ManualResetEvent (true);
         heardbeatActive = new ManualResetEvent (false);

         heardbeatThread = new Thread (IncrementHeartbeat);
         heardbeatThread.SetApartmentState (ApartmentState.MTA);
         heardbeatThread.Name = "Heardbeat_$" + Guid.NewGuid ().ToString ().Replace ('-', '_');

         heardbeatThread.Start ();
     }

     private SERVICE_STATUS DoQueryServiceStatus ()
     {
         SERVICE_STATUS serviceStatus = new SERVICE_STATUS();

         Interop.QueryServiceStatus (serviceHandle, ref serviceStatus);

         return serviceStatus;
     }


     public void StopHeardbeat ()
     {
         heardbeatActive.Reset ();
         heardbeatStopped.Set ();
     }

     public void StartHeardbeat ()
     {
         heardbeatStopped.Reset ();
         heardbeatActive.Set ();
     }

     private void IncrementHeartbeat ()
     {
         bool terminating = false;

         WaitHandle[] dispatchHandles = new WaitHandle[] { heardbeatStopped, heardbeatActive, terminationPending };
         WaitHandle[] activeHeardbeatHandles = new WaitHandle[] { heardbeatStopped, terminationPending };
         WaitHandle[] inactiveHeardbeatHandles = new WaitHandle[] { heardbeatActive, terminationPending };
         while (!terminating)
         {
         switch (WaitHandle.WaitAny (dispatchHandles))
         {
             case 0:
             WaitHandle.WaitAny (inactiveHeardbeatHandles);
             break;

             case 1:
             if (WaitHandle.WaitAny (activeHeardbeatHandles, heardbeatMilliseconds, false) == WAIT_TIMEOUT)
             {
                 SERVICE_STATUS serviceStatus = new SERVICE_STATUS ();

                 Interop.QueryServiceStatus (serviceHandle, ref serviceStatus);

                 serviceStatus.CheckPoint++;

                 Interop.SetServiceStatus (statusHandle, ref serviceStatus);
             }
             break;

         case 2:
             terminating = true;
             heardbeatStopped.Set ();
             break;
         }
         }
     }

     private void DoControlService (SERVICE_CONTROL aServiceControl)
     {
         SERVICE_STATUS serviceStatus = new SERVICE_STATUS();

         if (!Interop.ControlService(serviceHandle, aServiceControl, ref serviceStatus))
         {
         throw new Exception (serviceStatus.ToString ());
         }  
     }

     public void Dispose()
     {
         heardbeatActive.Reset ();
         heardbeatStopped.Reset ();
         terminationPending.Set ();

         heardbeatStopped.WaitOne ();

         Interop.CloseServiceHandle (serviceHandle);
         Interop.CloseServiceHandle (serviceControlManagerHandle);
     }
     }

     public class TestService : ServiceBase
     {
     ServiceControl serviceControl;

     public static void Main ()
     {
         ServiceBase.Run (new TestService());
     }

     public TestService()
     {  
         InitializeComponent ();
     }

     protected override void OnPause ()
     {
         serviceControl = new ServiceControl (this, 60000, 10000);
         serviceControl.StartHeardbeat ();
         //TODO: Add pause code here ...
         serviceControl.StopHeardbeat ();
     }

     private void InitializeComponent()
     {
         this.CanPauseAndContinue = true;
         this.ServiceName = "TestService";
     }

     protected override void OnStop()
     {
         serviceControl = new ServiceControl (this, 60000, 10000);
         serviceControl.StartHeardbeat ();
         //TODO: Add stop code here ...
         serviceControl.StopHeardbeat ();        
     }
     }

     internal class Interop
     {
     [DllImport ("advapi32.dll", SetLastError = true)]
     [return: MarshalAs (UnmanagedType.Bool)]
     internal static extern bool ControlService (
         IntPtr hService,
         SERVICE_CONTROL dwControl,
         ref SERVICE_STATUS lpServiceStatus);

     [DllImport ("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
     internal static extern IntPtr OpenSCManager (
         string machineName,
         string databaseName,
         uint dwAccess);

     [DllImport ("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
     internal static extern IntPtr OpenService (IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);

     [DllImport ("advapi32.dll", SetLastError = true)]
     [return: MarshalAs (UnmanagedType.Bool)]
     internal static extern bool CloseServiceHandle (IntPtr hSCObject);

     [DllImport ("advapi32.dll", EntryPoint = "QueryServiceStatus", CharSet = CharSet.Auto)]
     internal static extern bool QueryServiceStatus (IntPtr hService, ref SERVICE_STATUS dwServiceStatus);

     [DllImport ("advapi32.dll")]
     internal static extern bool SetServiceStatus (IntPtr hServiceStatus, ref SERVICE_STATUS lpServiceStatus);
     }
}

Documentation